#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/time.h>
#include <strings.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "hash.h"

static hash_table *mem_table = NULL;
static hash_link *mem_entry;
struct rusage myusage;

#ifdef WITH_LIB
#include "Mem.h"
#include <assert.h>
extern void sizeToPoolInit();
extern MemPool *sizeToPool(size_t size);
#endif
extern char *malloc_options;
void my_free(char *, int, void *);

FILE *fp;
char *fn;
int initsiz;
int maxsiz;
int minchunk;
HASHCMP ptrcmp;
char mbuf[256];
char abuf[32];
char *p;

int size;
void *addr;
int amt;

int i;
int a;
int run_stats = 0;
void *my_xmalloc(size_t);
void *my_xcalloc(int, size_t);
int my_xfree(void *);

#define xmalloc my_xmalloc
#define xcalloc my_xcalloc
#define xfree my_xfree

int *size2id_array[2];
int size2id_len = 0;
int size2id_alloc = 0;

typedef struct {
    char orig_ptr[32];
    void *my_ptr;
#ifdef WITH_LIB
    MemPool *pool;
#endif
    int size;
} memitem;

struct {
    int mallocs, frees, callocs, reallocs;
} mstat;

memitem *mi;
void size2id(size_t, memitem *);
void badformat();
void init_stats(), print_stats();
void my_hash_insert(hash_table * h, const char *k, memitem * item);
static void *xmemAlloc(memitem * item);
static void xmemFree(memitem * item);

int
ptrcmp(const void *a, const void *b)
{
    return (strcmp(a, b));
}

main(int argc, char **argv)
{
    char c;
    extern char *optarg;
    malloc_options = "A";
    a = 0;
    while ((c = getopt(argc, argv, "f:i:M:l:m:r:N")) != -1) {
	switch (c) {
	case 'N':
	    mem_pools_on = 0;
	    break;
	case 'r':
	    run_stats = atoi(optarg);
	    break;
	case 'f':
	    fn = strdup(optarg);
	    fp = fopen(fn, "r");
	    break;
	case 'i':
	    initsiz = atoi(optarg);
	    break;
	case 'l':
	    mem_max_size = atoi(optarg) * 1024 * 1024;
	    break;
	case 'M':
	    maxsiz = atoi(optarg);
	    break;
	case 'm':
	    minchunk = atoi(optarg);
	    break;
	default:
	    fprintf(stderr,
		"Usage: %s -f file -M maxsiz -i initsiz -m minchunk", argv[0]);
	    exit(1);
	}

    }
    if (!fp) {
	fprintf(stderr,
	    "%s pummels %s\n%s . o O ( You't supply a valid tracefile.)\n",
	    argv[0], getenv("USER"), argv[0]);
	exit(1);
    }
#ifdef WITH_LIB
    sizeToPoolInit();
#endif
    mem_table = hash_create(ptrcmp, 229, hash4);	/* small hash table */
    init_stats();
    while (fgets(mbuf, 256, fp) != NULL) {
	if (run_stats > 0 && (++a) % run_stats == 0)
	    print_stats();
	p = NULL;
	switch (mbuf[0]) {
	case 'm':		/* malloc */
	    p = strtok(&mbuf[2], ":");
	    if (!p)
		badformat();
	    size = atoi(p);
	    p = strtok(NULL, "\n");
	    if (!p)
		badformat();
	    mi = malloc(sizeof(memitem));
	    strcpy(mi->orig_ptr, p);
	    mi->size = size;
	    size2id(size, mi);
	    mi->my_ptr = xmemAlloc(mi);		/* (void *)xmalloc(size); */
	    assert(mi->my_ptr);
	    my_hash_insert(mem_table, mi->orig_ptr, mi);
	    mstat.mallocs++;
	    break;
	case 'c':		/* calloc */
	    p = strtok(&mbuf[2], ":");
	    if (!p)
		badformat();
	    amt = atoi(p);
	    p = strtok(NULL, ":");
	    if (!p)
		badformat();
	    size = atoi(p);
	    p = strtok(NULL, "\n");
	    if (!p)
		badformat();
	    mi = malloc(sizeof(memitem));
	    strcpy(mi->orig_ptr, p);
	    size2id(size, mi);
	    mi->size = amt * size;
	    mi->my_ptr = xmemAlloc(mi);		/*(void *)xmalloc(amt*size); */
	    assert(mi->my_ptr);
	    my_hash_insert(mem_table, mi->orig_ptr, mi);
	    mstat.callocs++;
	    break;
	case 'r':
	    p = strtok(&mbuf[2], ":");
	    if (!p)
		badformat();
	    strcpy(abuf, p);
	    p = strtok(NULL, ":");
	    if (!p)
		badformat();
	    mem_entry = hash_lookup(mem_table, p);
	    if (mem_entry == NULL) {
		fprintf(stderr, "invalid realloc (%s)!\n", p);
		break;
	    }
	    mi = (memitem *) (mem_entry->item);
	    assert(mi->pool);
	    assert(mi->my_ptr);
	    xmemFree(mi);	/* xfree(mi->my_ptr); */
	    size2id(atoi(p), mi);	/* we don't need it here I guess? */
	    strcpy(mi->orig_ptr, abuf);
	    p = strtok(NULL, "\n");
	    if (!p)
		badformat();
	    mi->my_ptr = xmemAlloc(mi);		/* (char *)xmalloc(atoi(p)); */
	    assert(mi->my_ptr);
	    mstat.reallocs++;
	    break;
	case 'f':
	    p = strtok(&mbuf[2], "\n");
	    mem_entry = hash_lookup(mem_table, p);
	    if (mem_entry == NULL) {
		if (p[0] != '0')
		    fprintf(stderr, "invalid free (%s) at line %d!\n", p, a);
		break;
	    }
	    mi = (memitem *) (mem_entry->item);
	    assert(mi->pool);
	    assert(mi->my_ptr);
	    xmemFree(mi);	/* xfree(mi->my_ptr); */
	    hash_unlink(mem_table, mem_entry, 1);
	    free(mi);
	    mstat.frees++;
	    break;
	default:
	    fprintf(stderr, "%s pummels %s.bad.format\n", argv[0], fn);
	    exit(1);
	}

    }
    fclose(fp);
    print_stats();
}

void *
my_xmalloc(size_t a)
{
    return NULL;
}

void *
my_xcalloc(int a, size_t b)
{
    return NULL;
}

int
my_xfree(void *p)
{
    return 0;
}
void
init_stats()
{

}

void
print_stats()
{
#ifdef WITH_LIB
    memReport(stdout);
#endif
    getrusage(RUSAGE_SELF, &myusage);
    printf("m/c/f/r=%d/%d/%d/%d\n", mstat.mallocs, mstat.callocs,
	mstat.frees, mstat.reallocs);
#if 0
    printf("types                 : %d\n", size2id_len);
#endif
    printf("user time used        : %d.%d\n", (int) myusage.ru_utime.tv_sec,
	(int) myusage.ru_utime.tv_usec);
    printf("system time used      : %d.%d\n", (int) myusage.ru_stime.tv_sec,
	(int) myusage.ru_stime.tv_usec);
    printf("max resident set size : %d\n", (int) myusage.ru_maxrss);
    printf("page faults           : %d\n", (int) myusage.ru_majflt);
}

void
size2id(size_t sz, memitem * mi)
{
#ifdef WITH_LIB
    mi->pool = sizeToPool(sz);
    assert(mi->pool);
#endif
    return;
}

void
badformat()
{
    fprintf(stderr, "pummel.bad.format\n");
    exit(1);
}

/* unused code, saved for parts */
const char *
make_nam(int id, int size)
{
    const char *buf = malloc(30);	/* argh */
    sprintf((char *) buf, "pl:%d/%d", id, size);
    return buf;
}

void
my_hash_insert(hash_table * h, const char *k, memitem * item)
{
    memitem *l;
    assert(item->pool);
    assert(item->my_ptr);
    hash_insert(h, k, item);
}

static void *
xmemAlloc(memitem * item)
{
    extern MemPool *StringPool;
    assert(item && item->pool);
    if (StringPool == item->pool)
	return memStringAlloc(item->pool, item->size);
    else
	return memAlloc(item->pool);
}

static void
xmemFree(memitem * item)
{
    extern MemPool *StringPool;
    assert(item && item->pool);
    if (StringPool == item->pool)
	return memStringFree(item->pool, item->my_ptr, item->size);
    else
	return memFree(item->pool, item->my_ptr);
}

void 
my_free(char *file, int line, void *ptr)
{
#if 0
    fprintf(stderr, "{%s:%d:%p", file, line, ptr);
#endif
    free(ptr);
#if 0
    fprintf(stderr, "}\n");
#endif
}
